package com.fxu.framework.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.fxu.framework.biz.util.LoginUtil;
import com.fxu.framework.biz.util.TokenUtil;
import com.fxu.framework.core.base.Cascade;
import com.fxu.framework.core.base.Constant;
import com.fxu.framework.core.enums.BaseEnum;
import com.fxu.framework.core.holder.RequestHolder;
import com.fxu.framework.core.holder.SpringHolder;
import com.fxu.framework.core.service.ApiService;
import com.fxu.framework.core.sql.*;
import com.fxu.framework.core.util.BeanUtil;
import com.fxu.framework.core.util.DiffUtil;
import com.fxu.framework.core.util.FieldUtil;
import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.query.MPJQueryWrapper;
import com.github.yulichang.toolkit.JoinWrappers;
import lombok.AllArgsConstructor;
import lombok.ToString;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.fxu.framework.core.util.FieldUtil.getColumnByBean;

/***
 * <p>fxu</p>
 * 业务逻辑接口实现
 * @author fangxu at 2021-08-07
 * @version 6.0.0 2021(tz: 6.0)、 2022-09-19
 ***/
@Component
public class ApiServiceImpl implements ApiService {
	protected Log log = LogFactory.getLog(ApiServiceImpl.class);
	/** MAPPER缓存 */
	private static final Map<Class<?>, BaseMapper<?>> BASE_MAPPER_CACHE = new ConcurrentHashMap<>();
	/** MAPPER缓存 */
	private static final Map<Class<?>, MPJBaseMapper<?>> MPJ_MAPPER_CACHE = new ConcurrentHashMap<>();
	// 表别名
	private static final String[] aliases = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s".split(StrPool.COMMA);

	// 每个Entity必须要有XXMapper
	@SuppressWarnings("unchecked")
	private <T> BaseMapper<T> getBaseMapper(Class<T> clazz) {
		if (BASE_MAPPER_CACHE.containsKey(clazz)) {
			return (BaseMapper<T>) BASE_MAPPER_CACHE.get(clazz);
		}

		String mapperName = clazz.getSimpleName() + "Mapper";
		boolean hasMapper = SpringHolder.hasBean(mapperName);
		BaseMapper<T> mapper = SpringHolder.getBean(hasMapper ? mapperName : StrUtil.lowerFirst(mapperName));
		BASE_MAPPER_CACHE.put(clazz, mapper);
		return mapper;
	}

	private <T> BaseMapper<T> getBaseMapper(Class<T> clazz, String table) {
		setDynamicTableName(table);
		return getBaseMapper(clazz);
	}

	@SuppressWarnings("unchecked")
	private <T> MPJBaseMapper<T> getMPJMapper(Class<T> clazz, String table) {
		setDynamicTableName(table);
		if (MPJ_MAPPER_CACHE.containsKey(clazz)) {
			return (MPJBaseMapper<T>)MPJ_MAPPER_CACHE.get(clazz);
		}

		String mapperName = clazz.getSimpleName() + "Mapper";
		boolean hasMapper = SpringHolder.hasBean(mapperName);
		MPJBaseMapper<T> mapper = SpringHolder.getBean(hasMapper ? mapperName : StrUtil.lowerFirst(mapperName));
		MPJ_MAPPER_CACHE.put(clazz, mapper);
		return mapper;
	}

	private <T> T copyNewBean(Class<T> clazz, T entity, List<ReUpdate> reUpdateList) {
		T newBean = ReflectUtil.newInstance(clazz);
		String idName = FieldUtil.getPriKeyName(clazz);
		BeanUtil.setFieldValue(newBean, idName, BeanUtil.getFieldValue(entity, idName));
		for (ReUpdate reUpdate : reUpdateList) {
			BeanUtil.setFieldValue(newBean, reUpdate.fieldName, reUpdate.fieldValue);
		}
		return newBean;
	}

	// 设置id值，并返回是否为null
	private <T> boolean checkAndSetId(Class<T> clazz, T entity) {
		Field keyField = FieldUtil.getPriKeyField(clazz);
		Object idVal = ReflectUtil.getFieldValue(entity, keyField);
		if (com.baomidou.mybatisplus.core.toolkit.StringUtils.checkValNull(idVal)) {
			if (keyField.getType() == String.class) {
				TableId tableId = keyField.getAnnotation(TableId.class);
				if (tableId == null || tableId.type() != IdType.AUTO) {
					String uuid = UUID.randomUUID().toString().replace("-", "");
					ReflectUtil.setFieldValue(entity, keyField.getName(), uuid);
				}
			}
			return true;
		}
		// 设置多租户ID
		getEntityByTid(entity);
		return false;
	}

	@Override
	@DSTransactional
	public <T extends SEntity<T>> boolean saveOne(T entity) {
		if (null == entity || null == entity.getClass()) {
			log.error("参数错误!entity=" + entity);
			return false;
		}
		@SuppressWarnings("unchecked")
		Class<T> clazz = (Class<T>) entity.getClass();
		BaseMapper<T> mapper = getBaseMapper(clazz, entity.getTable$());
		boolean isInsert = checkAndSetId(clazz, entity);
		if (!SqlHelper.retBool(isInsert ? mapper.insert(entity) : mapper.updateById(entity))) {
			return false;
		}
		// 级联保存
		List<ReUpdate> reUpdateList = cascade(clazz, entity, isInsert ? Save.Insert : Save.Update, false);
		if (reUpdateList.size() > 0) {
			return SqlHelper.retBool(mapper.updateById(copyNewBean(clazz, entity, reUpdateList)));
		}
		return true;
	}

	/**
	 * 多数据源中的事务，同时使用数据源1、2
	 * 如果这里用spring的事务注解Transactional，那么使用的是默认数据源
	 * 这里不需要加上注解@DS
	 * 同一个数据源中的事务，都是数据源2
	 * 这里用的是spring的事务注解Transactional
	 * 这里必须加上注解多数据源注解@DS("db2")，否则使用的是默认数据源
	 * @param <T> 实体对象列表类型
	 * @param entityList 实体对象列表
	 * @param doCascade 是否是级联
	 * @return 是否成功执行,成功返回true,失败返回false
	 */
	@DSTransactional
	//@Transactional(rollbackFor = Exception.class)
	public <T extends SEntity<?>> boolean saveBatch(Collection<T> entityList, boolean doCascade) {
		if (CollUtil.isEmpty(entityList) || BaseEnum.hasAny(entityList, e -> null == e || null == e.getClass())) {
			log.error("entity为空!entityList=" + entityList);
			return false;
		}
		@SuppressWarnings("unchecked")
		Class<T> clazz = (Class<T>) CollUtil.get(entityList, 0).getClass();
		BaseMapper<T> mapper = getBaseMapper(clazz);
		if (CollUtil.hasNull(entityList)) {
			log.error("参数错误entity=null!entityList=" + entityList);
			return false;
		}
		for (T entity : entityList) {
			boolean isInsert = checkAndSetId(clazz, entity);
			setDynamicTableName(entity.getTable$()); // 设置动态表名
			int back = isInsert ? mapper.insert(entity) : mapper.updateById(entity);
			if (!SqlHelper.retBool(back)) {
				throw new RuntimeException("执行失败!back=" + back + ",entity=" + entity);
			}
			if (doCascade) {
				List<ReUpdate> reList = cascade(clazz, entity, isInsert ? Save.Insert : Save.Update, false);
				if (reList.size() > 0) {
					int result = mapper.updateById(copyNewBean(clazz, entity, reList));
					if (!SqlHelper.retBool(result)) {
						throw new RuntimeException("执行失败!back=" + result);
					}
				}
			}
		}
		return true;
	}

	// 设置多租户ID
	private <T> T getEntityByTid(T entity) {
		if (entity == null) return null;
		if (ReflectUtil.hasField(entity.getClass(), Constant.NEW_TENANT_ID)) {
			ReflectUtil.setFieldValue(entity, Constant.NEW_TENANT_ID, getTidByToken());
		}
		return entity;
	}

	/**
	 * 忽略执行影响行数，保证效率
	 */
	@DSTransactional
	<T> boolean saveBatchIgnore(Collection<T> entityList, boolean doCascade) {
		if (CollUtil.isEmpty(entityList) || BaseEnum.hasAny(entityList, e -> null == e || null == e.getClass())) {
			log.error("entity为空!entityList=" + entityList);
			return false;
		}
		@SuppressWarnings("unchecked")
		Class<T> clazz = (Class<T>) CollUtil.get(entityList, 0).getClass();
		Class<?> mapperClass = SpringHolder.getType(StrUtil.lowerFirst(clazz.getSimpleName()) + "Mapper");

		List<T> insertList = new ArrayList<>();
		List<T> updateList = new ArrayList<>();
		for (T entity : entityList) {
			// 赋值ID值
			if (checkAndSetId(clazz, entity)) {
				insertList.add(entity);
			} else {
				updateList.add(entity);
			}
		}

		// 使用批量添加更新只能在默认库，分库不支持
		String insertSqlStatement = SqlHelper.getSqlStatement(mapperClass, SqlMethod.INSERT_ONE);
		boolean insertOk = SqlHelper.executeBatch(clazz, log, insertList, DEFAULT_BATCH_SIZE, (sqlSession, entity) ->
				sqlSession.insert(insertSqlStatement, entity));

		MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
		String updateSqlStatement = SqlHelper.getSqlStatement(mapperClass, SqlMethod.UPDATE_BY_ID);
		boolean updateOk = SqlHelper.executeBatch(clazz, log, updateList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> {
			param.put(Constants.ENTITY, entity);
			sqlSession.update(updateSqlStatement, param);
		});
		if ((insertOk || updateOk) && doCascade) {
			List<T> newUpdateList = new ArrayList<>();
			for (T bean : insertList) {
				List<ReUpdate> reUpdateList = cascade(clazz, bean, Save.Insert, true);
				if (reUpdateList.size() > 0) {
					newUpdateList.add(copyNewBean(clazz, bean, reUpdateList));
				}
			}
			for (T bean : updateList) {
				List<ReUpdate> reUpdateList = cascade(clazz, bean, Save.Update, true);
				if (reUpdateList.size() > 0) {
					newUpdateList.add(copyNewBean(clazz, bean, reUpdateList));
				}
			}
			if (newUpdateList.size() > 0) {
				return SqlHelper.executeBatch(clazz, log, newUpdateList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> {
					param.put(Constants.ENTITY, entity);
					sqlSession.update(updateSqlStatement, param);
				});
			}
		}
		return insertOk || updateOk;
	}

	@Override
	@DSTransactional
	public <SE extends SEntity<SE>> boolean saveBatch(Collection<SE> entityList) {
		return saveBatch(entityList, true);
	}

	@Override
	public <SE extends SEntity<SE>> boolean saveBatchIgnore(Collection<SE> entityList) {
		return saveBatchIgnore(entityList, true);
	}

	@DSTransactional
	public <SE extends SEntity<SE>> boolean insertBatch(@NotNull Collection<SE> entityList, boolean ignore) {
		if (CollUtil.isEmpty(entityList) || BaseEnum.hasAny(entityList, e -> null == e || null == e.getClass())) {
			log.error("entity为空!entityList=" + entityList);
			return false;
		}
		@SuppressWarnings("unchecked")
		Class<SE> clazz = (Class<SE>) CollUtil.get(entityList, 0).getClass();
		if (ignore) {
			Class<?> mapperClass = SpringHolder.getType(StrUtil.lowerFirst(clazz.getSimpleName()) + "Mapper");
			for (SE entity : entityList) {
				getEntityByTid(entity);
			}
			// 使用批量添加更新只能在默认库，分库不支持
			String insertSqlStatement = SqlHelper.getSqlStatement(mapperClass, SqlMethod.INSERT_ONE);
			return SqlHelper.executeBatch(clazz, log, entityList, DEFAULT_BATCH_SIZE, (sqlSession, entity) ->
					sqlSession.insert(insertSqlStatement, entity));
		} else {
			BaseMapper<SE> mapper = getBaseMapper(clazz);
			for (SE entity : entityList) {
				setDynamicTableName(entity.getTable$()); // 设置动态表名
				if (!SqlHelper.retBool(mapper.insert(entity))) {
					throw new RuntimeException("执行失败!entity=" + entity);
				}
			}
			return true;
		}
	}

	@Override
	@DSTransactional
	public boolean saveBatchTx(Collection<Object> entityList) {
		if (CollUtil.isEmpty(entityList) || BaseEnum.hasAny(entityList, e -> null == e || null == e.getClass())) {
			log.error("entity为空!entityList=" + entityList);
			return false;
		}
		// 多数据源没法事务，且必须在第一个默认数据库中，暂时解决不了，使用saveOne多个保存吧
		for (Object entity : entityList) {
			@SuppressWarnings("unchecked")
			Class<Object> clazz = (Class<Object>) entity.getClass();
			BaseMapper<Object> mapper = getBaseMapper(clazz);
			Object table$ = BeanUtil.getFieldValue(entity, "table$");
			setDynamicTableName(table$ == null ? null : table$.toString()); // 设置动态表名
			int back = checkAndSetId(clazz, entity) ? mapper.insert(entity) : mapper.updateById(entity);
			if (!SqlHelper.retBool(back)) {
				throw new RuntimeException("执行失败!back=" + back + ",entity=" + entity);
			}
		}
		return true;
	}

	@Override
	public <SE extends SEntity<SE>> boolean updateBy(SUpdate<SE> chain) {
		String result = checkUpdateChain(chain);
		if (null != result) {
			log.error(result);
			return false;
		}
		return SqlHelper.retBool(getBaseMapper(chain.getClazz(), chain.getTable())
				.update(null, getUpdateWrapper(chain)));
	}

	private String checkUpdateChain(SUpdate<?> chain) {
		if (chain == null || chain.getClazz() == null) {
			return "参数错误!updateChain=" + chain;
		}
		if (CollUtil.isEmpty(chain.getQueryList())) {
			return "必须含有查询条件!updateChain=" + chain;
		}
		if (CollUtil.isEmpty(chain.getUpdateList())) {
			return "必须含有更新字段!updateChain=" + chain;
		}
		return null;
	}

	@Override
	@DSTransactional
	public <SE extends SEntity<SE>> boolean updateBatchBy(Collection<SUpdate<SE>> chains) {
		if (CollUtil.isEmpty(chains) || BaseEnum.hasAny(chains, e -> null == e || null == e.getClazz())) {
			log.error("参数错误!updateChainList=" + chains);
			return false;
		}
		for (SUpdate<SE> chain : chains) {
			String result = checkUpdateChain(chain);
			if (null != result) {
				throw new RuntimeException(result);
			}
			int back = getBaseMapper(chain.getClazz(), chain.getTable()).update(null, getUpdateWrapper(chain));
			if (!SqlHelper.retBool(back)) {
				throw new RuntimeException("运行错误!updateChain=" + chain + ",back=" + back);
			}
		}
		return true;
	}

	@Override
	@DSTransactional
	public boolean updateBatch(Collection<SUpdate<?>> chains) {
		if (CollUtil.isEmpty(chains) || BaseEnum.hasAny(chains, e -> null == e || null == e.getClazz())) {
			log.error("参数错误!updateChainList=" + chains);
			return false;
		}
		for (SUpdate<?> chain : chains) {
			String result = checkUpdateChain(chain);
			if (null != result) {
				throw new RuntimeException(result);
			}
			int back = getBaseMapper(chain.getClazz(), chain.getTable()).update(null, getUpdateWrapper(chain));
			if (!SqlHelper.retBool(back)) {
				throw new RuntimeException("运行错误!updateChain=" + chain + ",back=" + back);
			}
		}
		return true;
	}

	@Override
	public <SE extends SEntity<SE>> int deleteBy(SQuery<SE> query) {
		Class<SE> clazz = getClazz(query);
		QueryWrapper<SE> deleteWrapper = getDeleteWrapper(query);
		// 逻辑删除
		if (ReflectUtil.hasField(clazz, Constant.CLN_STATUS)) {
			SE entity = ReflectUtil.newInstance(clazz);
			Field field = ReflectUtil.getField(clazz, Constant.CLN_STATUS);
			if (String.class == field.getType()) {
				BeanUtil.setFieldValue(entity, Constant.CLN_STATUS, Constant.DELETE_STATUS_STR);
			} else if (Integer.class == field.getType()) {
				BeanUtil.setFieldValue(entity, Constant.CLN_STATUS, Constant.DELETE_STATUS);
			} else {
				throw new RuntimeException("status类型错误!type=" + field.getType());
			}
			return getBaseMapper(clazz, query.getTable()).update(entity, deleteWrapper);
		} else {
			// 物理删除
			return getBaseMapper(clazz, query.getTable()).delete(deleteWrapper);
		}
	}

	private <T extends SEntity<?>> Class<T> getClazz(SQuery<T> query) {
		if (null == query || null == query.getClazz()) {
			throw new RuntimeException("参数错误!query=null或者query.getClazz()=null");
		}
		return query.getClazz();
	}

	public <SE extends SEntity<SE>> List<SE> getList(Class<SE> clazz, String table, AbstractWrapper<SE, String, ?> wrapper) {
		if (wrapper.getClass() == MPJQueryWrapper.class) {
			@SuppressWarnings("unchecked")
			MPJQueryWrapper<SE> newWrapper = (MPJQueryWrapper<SE>) wrapper;
			return getMPJMapper(clazz, table).selectJoinList(clazz, newWrapper);
		} else {
			return getBaseMapper(clazz, table).selectList(wrapper);
		}
	}

	@Override
	public <SE extends SEntity<SE>> List<SE> listBy(SQuery<SE> query) {
		if (hasNoQuery(query)) return new ArrayList<>();
		AbstractWrapper<SE, String, ?> wrapper = getQueryWrapper(query, true);
		List<SE> list = getList(getClazz(query), query.getTable(), wrapper);
		return fillBean(list, query);
	}

	@Override
	@SuppressWarnings("unchecked")
	public <SE extends SEntity<SE>> Page<SE> pageBy(int page, int size, SQuery<SE> query) {
		page = page < 1 ? Constant.DEFAULT_PAGE_NUM : page;
		size = size < 1 ? Constant.DEFAULT_PAGE_SIZE : size;

		if (hasNoQuery(query)) return new Page<>(page, size, 0, false);
		// selectPage 数据为空返回page.getRecords()的是=>[]
		AbstractWrapper<SE, String, ?> wrapper = getQueryWrapper(query, true);
		Page<SE> pageInfo;
		if (wrapper.getClass() == MPJQueryWrapper.class) {
			pageInfo = getMPJMapper(getClazz(query), query.getTable()).selectJoinPage(new Page<>(page, size),
					query.getClazz(), (MPJQueryWrapper<SE>) wrapper);
		} else {
			pageInfo = getBaseMapper(getClazz(query), query.getTable()).selectPage(new Page<>(page, size), wrapper);
		}
		fillBean(pageInfo.getRecords(), query);
		return pageInfo;
	}

	@Override
	public <SE extends SEntity<SE>> List<SE> limitBy(int page, int size, SQuery<SE> query) {
		page = page < 1 ? Constant.DEFAULT_PAGE_NUM : page;
		size = size < 1 ? Constant.DEFAULT_PAGE_SIZE : size;

		if (hasNoQuery(query)) return new ArrayList<>();
		AbstractWrapper<SE, String, ?> wrapper = getQueryWrapper(query, true);
		wrapper.last("LIMIT " + ((page - 1) * size) + "," + size);
		List<SE> list = getList(getClazz(query), query.getTable(), wrapper);
		return fillBean(list, query);
	}

	@Override
	public <SE extends SEntity<SE>> List<SE> offsetBy(int size, Long nextId, SQuery<SE> query) {
		size = size < 1 ? Constant.DEFAULT_PAGE_SIZE : size;

		if (hasNoQuery(query)) return new ArrayList<>();
		AbstractWrapper<SE, String, ?> wrapper = getQueryWrapper(query, true);
		if (nextId != null && nextId > 0) {
			if (isIdDesc(query)) {
				wrapper.lt(query.getPriKey(), nextId);
			} else {
				wrapper.gt(query.getPriKey(), nextId);
			}
		}
		wrapper.last("LIMIT " + size);
		List<SE> list = getList(getClazz(query), query.getTable(), wrapper);
		return fillBean(list, query);
	}

	@Override
	@SuppressWarnings("unchecked")
	public <SE extends SEntity<SE>> long countBy(SQuery<SE> query) {
		if (hasNoQuery(query)) return 0L;
		Long count;
		AbstractWrapper<SE, String, ?> wrapper = getQueryWrapper(query.notOrderBy(), false);
		if (wrapper.getClass() == MPJQueryWrapper.class) {
			count = getMPJMapper(getClazz(query), query.getTable()).selectJoinCount((MPJQueryWrapper<SE>) wrapper);
		} else {
			count = getBaseMapper(getClazz(query), query.getTable()).selectCount(wrapper);
		}
		return null == count ? 0 : count;
	}

	@Override
	public <SE extends SEntity<SE>> SE sumsBy(SQuery<SE> query) {
		Class<SE> clazz = getClazz(query);
		if (hasNoQuery(query)) return ReflectUtil.newInstance(clazz);
		return getBaseMapper(clazz, query.getTable()).selectOne(getGroupSumWrapper(query, true));
	}

	@Override
	public <SE extends SEntity<SE>> List<SE> groupSumBy(SQuery<SE> query) {
		if (hasNoQuery(query)) return new ArrayList<>();
		return getBaseMapper(getClazz(query), query.getTable()).selectList(getGroupSumWrapper(query, false));
	}

	@Override
	public <SE extends SEntity<SE>> List<SE> groupSumBy(int size, SQuery<SE> query) {
		if (hasNoQuery(query)) return new ArrayList<>();
		QueryWrapper<SE> wrapper = getGroupSumWrapper(query, false);
		wrapper.last("LIMIT " + size);
		return getBaseMapper(getClazz(query), query.getTable()).selectList(wrapper);
	}

	@Override
	public <SE extends SEntity<SE>> Page<SE> groupSumBy(int page, int size, SQuery<SE> query) {
		if (hasNoQuery(query)) return new Page<>(page, size, 0, false);
		return getBaseMapper(getClazz(query), query.getTable()).selectPage(new Page<>(page, size),
				getGroupSumWrapper(query, false));
	}

	public <SE extends SEntity<SE>> QueryWrapper<SE> getGroupSumWrapper(SQuery<SE> query, boolean sum) {
		if (sum && (CollUtil.isEmpty(query.getSumFields()) || query.getSumFields().get(0) == null)) {
			throw new RuntimeException("必须传入sumField！query=" + query);
		}
		if (!sum && (CollUtil.isEmpty(query.getGrpFields()) || query.getGrpFields().get(0) == null)) {
			throw new RuntimeException("必须传入grpField！query=" + query);
		}

		List<String> sumFields = getColumns(query.getSumFields());
		List<String> grpFields = getColumns(query.getGrpFields());

		@SuppressWarnings("unchecked")
		QueryWrapper<SE> wrapper = (QueryWrapper<SE>) getQueryWrapper(query.notOrderBy(), false);

		query.getOrderByFields().forEach(sValue -> {
			String clnName = sValue.getField().getColumnName();
			if (sumFields.contains(clnName) || grpFields.contains(clnName) || query.getPriKey().equals(clnName)) {
				wrapper.orderBy(true, sValue.getSType() == SType.ORDER_BY_ASC, clnName);
			} else {
				log.error("字段:" + sValue.getField().getName() + ",不在sumFields和grpFields中,不能用于排序");
			}
		});
		String tpl = "IFNULL(sum({}), 0) as {}";
		if (CollUtil.isNotEmpty(grpFields)) {
			grpFields.add("count(0) as " + query.getPriKey());
			grpFields.addAll(sumFields.stream().map(v -> StrUtil.format(tpl, v, v)).collect(Collectors.toList()));
			wrapper.select(ArrayUtil.toArray(grpFields, String.class));
		} else {
			List<String> clns = sumFields.stream().map(v -> StrUtil.format(tpl, v, v)).collect(Collectors.toList());
			clns.add("count(0) as " + query.getPriKey());
			wrapper.select(clns.toArray(new String[0]));
		}
		return wrapper;
	}

	/**
	 * JavaBean对象的级联对象添加更新
	 * @param clazz JavaBean对象类
	 * @param bean JavaBean内容map
	 * @param save 操作枚举类型
	 * @param ignore 是否忽略执行行数
	 * @return 返回是否需要更新本体Entity
	 */
	@SuppressWarnings({"unchecked", "rawtypes"})
	@DSTransactional
	private <SE> List<ReUpdate> cascade(Class<SE> clazz, SE bean, Save save, boolean ignore) {
		Class<?> linkClazz; Object linkFrom, fieldVal;
		Cascade cascade; List<Object> newList, idList; Object idVal;
		List<ReUpdate> reUpdateList = new ArrayList<>();
		// getFieldsDirectly 不带缓存，可以排除父类，getFields 读取父类，但是有缓存
		for (Field field : ReflectUtil.getFields(clazz)) {
			cascade = FieldUtil.getCascade(field);
			if (cascade == null || !cascade.save()) continue;
			fieldVal = BeanUtil.getFieldValue(bean, field.getName());
			linkFrom = BeanUtil.getFieldValue(bean, cascade.thisField());
			//log.debug("cascade.linkFrom===>" + linkFrom + ",thisField=" + cascade.thisField() + ",bean=" + bean);
			if (fieldVal == null || linkFrom == null) continue;
			linkClazz = List.class == field.getType() ? FieldUtil.getFieldClass(field) : field.getType();

			if (save == Save.Insert || save == Save.Update) {
				// 一对多级联
				if (List.class == field.getType()) {
					newList = (List<Object>) fieldVal;
					idList = new ArrayList<>(newList.size());
					if (newList.size() > 0) {
						for (Object obj : newList) {
							//给级联对象连接字段赋值
							BeanUtil.setFieldValue(obj, cascade.linkField(), linkFrom);
							idVal = BeanUtil.getFieldValue(obj, FieldUtil.getPriKeyName(linkClazz));
							if (idVal != null) {
								idList.add(idVal);
							}
						}
					}
					// 删除旧数据[排除传入的ID]
					SQuery<?> sQuery = new SQuery(linkClazz).where(SType.EQ, cascade.linkField(), linkFrom);
					// 有可能旧数据没有, 所以不能 <= 0, 只能 < 0
					if (save == Save.Update && this.deleteBy((SQuery) sQuery.notIn(SQuery::idFun, idList)) < 0) {
						throw new RuntimeException(StrUtil.format("cascade[同步删除错误，{}.{}={}错误]",
								linkClazz, cascade.linkField(), linkFrom));
					}

					// 排除相同的数据不更新
					if (newList.size() > 0 && idList.size() > 0) {
						SQuery diffQuery = (SQuery)new SQuery(linkClazz).in(SQuery::idFun, idList);
						newList = DiffUtil.getDiff(newList, listBy(diffQuery));
					}

					// 需要测试级联 保存新数据
					boolean noCascade = false;
					if (newList.size() > 0 && !(ignore ? saveBatchIgnore(newList, noCascade) : saveBatchTx(newList))) {
						throw new RuntimeException("cascade[同步保存错误，保存=" + newList + "错误]");
					}
				}
				// 一对一级联
				else if (String.class != field.getType()) {
					//给级联对象连接字段赋值
					BeanUtil.setFieldValue(fieldVal, cascade.linkField(), linkFrom);
					if (!this.saveBatchTx(CollUtil.toList(fieldVal))) {
						throw new RuntimeException("cascade[同步保存错误，保存对象=" + fieldVal + "错误]");
					}
					String thisIdName = FieldUtil.getPriKeyName(clazz);
					String linkIdName = FieldUtil.getPriKeyName(linkClazz);
					if (!thisIdName.equals(cascade.thisField()) && linkIdName.equals(cascade.linkField())) {
						// 判断id是否改变
						Object oldFieldVal = BeanUtil.getFieldValue(bean, cascade.thisField());
						Object newFieldVal = BeanUtil.getFieldValue(fieldVal, linkIdName);
						if (oldFieldVal == null // || (oldFieldVal != null && newFieldVal == null)
								|| (newFieldVal != null && !oldFieldVal.toString().equals(newFieldVal.toString()))) {
							//BeanUtil.setFieldValue(bean, cascade.thisField(), newFieldVal);
							reUpdateList.add(new ReUpdate(cascade.thisField(), newFieldVal));
						}
					}
				}
			} else {
				//TODO 暂时不支持级联删除[查找所有级联对象然后删除]
				if (String.class != field.getType()) {
					//读取旧数据进行删除[可能没有一个]
					if (this.deleteBy(new SQuery(linkClazz).where(SType.EQ, cascade.linkField(), linkFrom)) < 0) {
						throw new RuntimeException(StrUtil.format("cascade[同步删除错误，{}.{}={}错误]",
								linkClazz, cascade.linkField(), linkFrom));
					}
				}
			}
		}
		return reUpdateList;
	}

	/**
	 * 填充对象
	 * @param beans 对象集合
	 * @param sQuery 级联读取集合
	 * @return null => 成功, != null 返回错误信息
	 */
	@SuppressWarnings({"unchecked", "rawtypes"})
	private <T extends SEntity<?>> List<T> fillBean(List<T> beans, SQuery<T> sQuery) {
		if (beans == null || sQuery == null || sQuery.getClazz() == null) {
			return new ArrayList<>();
		}
		Class<T> clazz = sQuery.getClazz();
		if (CollUtil.isEmpty(beans) || MapUtil.isEmpty(sQuery.getCascadeMap())) {
			return beans;
		}
		Field field; Cascade cascade;
		Object linkFrom; String fieldName;
		Map<String, FillBeanVo> casMap = new HashMap<>();

		for (T bean : beans) {
			for (SField<?> sField : sQuery.getCascadeMap().keySet()) {
				fieldName = sField.getName();

				field = ReflectUtil.getField(bean.getClass(), fieldName);
				if (field == null) {
					// 还有特定交给***RestImpl自己处理
					continue;
				}
				cascade = FieldUtil.getCascade(field);
				if (cascade == null) {
					throw new RuntimeException("类["+clazz+"]这个字段["+fieldName+"]没有级联@Cascade！");
				}
				linkFrom = BeanUtil.getFieldValue(bean, cascade.thisField());
				if (linkFrom == null || StrUtil.isEmpty(linkFrom.toString())) {
					continue;
				}

				List<Object> arr;
				if (List.class.isAssignableFrom(linkFrom.getClass())) {
					arr = new ArrayList<>((List<Object>) linkFrom);
				} else if (linkFrom.toString().contains(StrUtil.COMMA)) {
					arr = StrUtil.split(linkFrom.toString(), StrUtil.COMMA)
							.stream().map(e -> (Object) e).collect(Collectors.toList());
				} else {
					arr = CollUtil.toList(linkFrom);
				}

				// role.user, user.role, user.roleList
				if (casMap.get(fieldName) == null) {
					casMap.put(fieldName, new FillBeanVo(cascade, field, sQuery.getCascadeMap().get(sField), arr));
				} else {
					for (Object linkObj : arr) {
						if (!casMap.get(fieldName).values.contains(linkObj)) {
							casMap.get(fieldName).values.add(linkObj);
						}
					}
				}
			}
		}
		if (MapUtil.isEmpty(casMap)) {
			// log.debug("casMap={}", casMap);
			return beans;
		}
		FillBeanVo fbVo; List<T> list;
		for (String key : casMap.keySet()) {
			fbVo = casMap.get(key);
			list = listBy((SQuery) new SQuery(fbVo.linkClass).where(SType.IN, fbVo.linkField, fbVo.values)
					.show(fbVo.showFields).orderNo(OrderNo.ASC).orderAsc(SQuery::idFun));
			for (T bean : beans) {
				BeanUtil.setFieldValue(bean, key, fbVo.getValue(list, BeanUtil.getFieldValue(bean, fbVo.thisField)));
			}
		}
		return beans;
	}

	/**
	 * fillBean 填充对象函数用到的类
	 * @author fangxu
	 */
	@ToString
	static class FillBeanVo {
		/** 级联链接类 */
		Class<?> linkClass;
		/** 级联本Entity类的字段 */
		String thisField;
		/** 级联链接Entity类的字段 */
		String linkField;
		/** 级联链接Entity类的显示字段 */
		List<SField<?>> showFields;
		/** 级联链接查询值集合(去重) */
		List<Object> values;
		/** 是否是List属性 */
		boolean isList;

		public FillBeanVo(Cascade cascade, Field field, List<SField<?>> showFields, List<Object> ids) {
			this.thisField = cascade.thisField();
			this.linkField = cascade.linkField();
			this.isList = List.class == field.getType();
			this.linkClass = isList ? FieldUtil.getFieldClass(field) : field.getType();
			this.showFields = CollUtil.isNotEmpty(showFields) ? showFields : new ArrayList<>();
			// 定制查询字段没有[关联字段]必须增加[关联字段]
			if (CollUtil.isNotEmpty(showFields) && CollUtil.findOne(this.showFields,
					f -> f.getName().equals(linkField)) == null) {
				this.showFields.add(new SField<>(this.linkClass, linkField));
			}
			this.values = ids;
		}

		@SuppressWarnings("unchecked")
		public <T extends SEntity<?>> Object getValue(List<T> rets, Object filedVal) {
			if (CollUtil.isEmpty(rets) || filedVal == null) {
				return null;
			}
			List<Object> valueArr = new ArrayList<>();
			if (List.class.isAssignableFrom(filedVal.getClass())) {
				valueArr = (List<Object>) filedVal;
			} else {
				String fieldValue = String.valueOf(filedVal);
				valueArr.addAll(StrUtil.split(fieldValue, StrUtil.COMMA));
			}

			Object val; List<Object> bkList = new ArrayList<>();
			for (Object result : rets) {
				val = BeanUtil.getFieldValue(result, linkField);
				String valStr = val == null ? null : String.valueOf(val);
				if (valStr != null && CollUtil.findOne(valueArr, v -> v.toString().equals(valStr)) != null) {
					if (!isList) {
						return result;
					}
					bkList.add(result);
				}
			}
			return !isList || bkList.size() <= 0 ? null : bkList;
		}
	}

	// 删除条件
	private <T extends SEntity<?>> QueryWrapper<T> getDeleteWrapper(SQuery<T> query) {
		if (query == null || query.getClazz() == null) {
			throw new RuntimeException("参数错误!");
		}
		Class<T> clazz = query.getClazz();
		QueryWrapper<T> wrapper = new QueryWrapper<>();
		if (CollUtil.isNotEmpty(query.getQueryList())) {
			query.getQueryList().forEach(sValue -> setQueryWrapper(clazz, wrapper, sValue));
		}
		if (ReflectUtil.hasField(clazz, Constant.NEW_TENANT_ID)) {
			SValue sValue = new SValue(SType.EQ, new SField<>(clazz, Constant.NEW_TENANT_ID), getTidByToken());
			setQueryWrapper(clazz, wrapper, sValue);
		}
		return wrapper;
	}

	private <T extends SEntity<?>> UpdateWrapper<T> getUpdateWrapper(SUpdate<?> sUpdate) {
		if (sUpdate == null || sUpdate.getClazz() == null || CollUtil.isEmpty(sUpdate.getQueryList())) {
			throw new RuntimeException("参数错误!");
		}
		@SuppressWarnings("unchecked")
		Class<T> clazz = (Class<T>) sUpdate.getClazz();
		String udSql = "{} = IFNULL({}, 0) {} {}";
		UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
		for (SValue sVal : sUpdate.getUpdateList()) {
			// 允许更新ID || idName.equals(sVal.getField().getFieldName())
			if (sVal == null || sVal.getField() == null || sVal.getField().getName() == null
					|| sVal.getSType() == null) {
				throw new RuntimeException("updateChain.updateList有问题!value=" + sVal);
			}
			String cln = sVal.getField().getColumnName();
			if (sVal.getSType() == SType.UP || sVal.getSType() == SType.DN) {
				updateWrapper.setSql(StrUtil.format(udSql, cln, cln, sVal.getSType().getKey(), sVal.getValue()));
			} else if (sVal.getSType() == SType.SET) {
				updateWrapper.set(cln, sVal.getValue());
			} else {
				throw new RuntimeException("类型错误!type=" + sVal.getSType());
			}
		}
		if (CollUtil.isNotEmpty(sUpdate.getQueryList())) {
			sUpdate.getQueryList().forEach(sValue -> setQueryWrapper(clazz, updateWrapper, sValue));
		}
		if (ReflectUtil.hasField(clazz, Constant.NEW_TENANT_ID)) {
			SValue sValue = new SValue(SType.EQ, new SField<>(clazz, Constant.NEW_TENANT_ID), getTidByToken());
			setQueryWrapper(clazz, updateWrapper, sValue);
		}
		return updateWrapper;
	}

	// 复杂sql, 目前包含 in sql, left join, right join, inner join, full join
	private <T extends SEntity<?>> void setApplyWrapper(boolean hasJoin, AbstractWrapper<T, String, ?> qWrapper,
														List<SApply<?, T>> applyList) {
		if (qWrapper == null || CollUtil.isEmpty(applyList)) return;
		for (int i = 0; i < applyList.size(); i++) {
			SApply<?, T> s = applyList.get(i);
			String alias = aliases[i];
			Class<?> oClass = s.getClazz();
			JoinType type = s.getJoinType();
			String bTable = getTableName(oClass);
			String bColumn = s.getThisField().getColumnName();
			String aColumn = s.getOtherField().getColumnName();
			// 子项中是否能andOr查询
			if (type == JoinType.LEFT || type == JoinType.RIGHT || type == JoinType.INNER) {
				// 添加租户、逻辑状态查询条件
				addQuery(oClass, s.getQueryList());
				s.getQueryList().forEach(v -> setQueryWrapper(oClass, qWrapper, v, alias));
				s.getAndOrList().forEach(v -> setAndOrWrapper(oClass, qWrapper, v.getQueryList(), alias));

				@SuppressWarnings("unchecked")
				MPJQueryWrapper<T> sWrapper = (MPJQueryWrapper<T>) qWrapper;
				String template = "{} {} on {}.{} = t.{}";
				String joinSql = StrUtil.format(template, bTable, alias, alias, bColumn, aColumn);
				sWrapper.join(type + " JOIN", true, joinSql);
			} else if (type == JoinType.INSQL || type == JoinType.NOTINSQL) {
				String childAlias = hasJoin ? alias : null;
				QueryWrapper<?> bWrapper = new QueryWrapper<>();
				// 添加租户、逻辑状态查询条件
				addQuery(oClass, s.getQueryList());
				s.getQueryList().forEach(v -> setQueryWrapper(oClass, bWrapper, v, childAlias));
				s.getAndOrList().forEach(v -> setAndOrWrapper(oClass, bWrapper, v.getQueryList(), childAlias));

				String sql = StrUtil.format("SELECT DISTINCT {} FROM {} {}", bColumn, bTable, toSql(bWrapper));
				if (CollUtil.isNotEmpty(s.getQueryList()) || CollUtil.isNotEmpty(s.getAndOrList())) {
					if (type == JoinType.INSQL) {
						qWrapper.inSql(aColumn, sql);
					} else {
						qWrapper.notInSql(aColumn, sql);
					}
				}
			}
		}
	}

	// 添加租户、逻辑状态查询条件
	private void addQuery(Class<?> clazz, List<SValue> queryList) {
		// 多租户ID查询条件
		if (ReflectUtil.hasField(clazz, Constant.NEW_TENANT_ID) && !BaseEnum.hasAny(queryList,
				c -> Constant.NEW_TENANT_ID.equals(c.getField().getColumnName()))) {
			Field field = ReflectUtil.getField(clazz, Constant.NEW_TENANT_ID);
			queryList.add(new SValue(SType.EQ, new SField<>(field), getTidByToken()));
		}

		// 逻辑删除(如果已经有了status的参数，则不加入逻辑删除[加入则多余])
		if (ReflectUtil.hasField(clazz, Constant.CLN_STATUS) && !BaseEnum.hasAny(queryList,
				c -> Constant.CLN_STATUS.equals(c.getField().getColumnName()))) {
			Field field = ReflectUtil.getField(clazz, Constant.CLN_STATUS);
			if (String.class == field.getType()) {
				queryList.add(new SValue(SType.NE, new SField<>(field), Constant.DELETE_STATUS_STR));
			} else if (Integer.class == field.getType()) {
				queryList.add(new SValue(SType.GT, new SField<>(field), Constant.DELETE_STATUS));
			}
		}
	}

	private void setAndOrWrapper(Class<?> clazz, AbstractWrapper<?, String, ?> qWrapper, List<SValue> queryList,
								 String alias) {
		if (qWrapper != null && !CollUtil.isEmpty(queryList)) {
			qWrapper.and(wrapper -> queryList.forEach(sValue -> {
				wrapper.or(); setQueryWrapper(clazz, wrapper, sValue, alias);
			}));
		}
	}

	/**
	 * 获得SEoken，有可能返回null
	 * @return 返回Token
	 */
	private String getToken() {
		return TokenUtil.getToken(RequestHolder.getRequestMayNull());
	}

	/**
	 * 获得租户ID，获取不到数据返回0
	 * @return 返回租户ID
	 */
	private long getTidByToken() {
		return com.fxu.framework.core.util.MapUtil.getLong(LoginUtil.getInfo(getToken()), Constant.NEW_TENANT_ID, 0L);
	}

	// 根据getText和map还有orderItems组合成QueryWrapper
	private <T extends SEntity<T>> AbstractWrapper<T, String, ?> getQueryWrapper(SQuery<T> query, boolean select) {
		boolean hasJoin = hasJoin(query);
		Class<T> clazz = getClazz(query);
		AbstractWrapper<T, String, ?> wrapper = hasJoin ? JoinWrappers.query(clazz) : new QueryWrapper<>();
		if (hasJoin && select) {
			@SuppressWarnings("unchecked")
			MPJQueryWrapper<T> newWrapper = (MPJQueryWrapper<T>) wrapper;
			if (query.isOnlyShowId()) {
				newWrapper.select("t." + getColumnByBean(clazz, query.getPriKey()));
			} else if (CollUtil.isNotEmpty(query.getShowFields())) {
				newWrapper.select(true, getColumns(query.getShowFields()).stream().map(v -> "t." + v)
						.collect(Collectors.toList()));
			} else if (query.isShowDefault()) {
				newWrapper.select(clazz, info -> !SPECIAL_COLUMNS.contains(info.getColumn()));
			} else if (query.isShowText() || !FieldUtil.hasTxtField(clazz)) {
				newWrapper.select("t.*"); // 优化考虑，不建议使用
				// queryWrapper.select(clazz, info -> true);
			}
		} else if (select) {
			@SuppressWarnings("unchecked")
			QueryWrapper<T> newWrapper = (QueryWrapper<T>) wrapper;
			if (query.isOnlyShowId()) {
				newWrapper.select(getColumnByBean(clazz, query.getPriKey()));
			} else if (CollUtil.isNotEmpty(query.getShowFields())) {
				newWrapper.select(ArrayUtil.toArray(getColumns(query.getShowFields()), String.class));
			} else if (query.isShowDefault()) {
				newWrapper.select(clazz, info -> !SPECIAL_COLUMNS.contains(info.getColumn()));
			} else if (query.isShowText() || !FieldUtil.hasTxtField(clazz)) {
				newWrapper.select("*"); // 优化考虑，不建议使用
				// queryWrapper.select(clazz, info -> true);
			}
		}

		// 添加租户、逻辑状态查询条件
		addQuery(clazz, query.getQueryList());

		// 关联查询条件
		query.getQueryList().forEach(v -> setQueryWrapper(clazz, wrapper, v, hasJoin ? "t" : null));
		query.getAndOrList().forEach(v -> setAndOrWrapper(clazz, wrapper, v.getQueryList(), hasJoin ? "t" : null));
		setApplyWrapper(hasJoin, wrapper, query.getApplyList());

		// group by
		if (CollUtil.isNotEmpty(query.getGrpFields())) {
			if (hasJoin) {
				wrapper.groupBy(getColumns(query.getGrpFields()).stream()
						.map(v -> "t." + v).collect(Collectors.toList()));
			} else {
				wrapper.groupBy(getColumns(query.getGrpFields()));
			}
		}

		// 定制查询排序
		if (!query.isNotOrderBy()) {
			if (query.getOrderNo() != null && ReflectUtil.hasField(clazz, Constant.FLD_ORDER_NO)) {
				wrapper.orderBy(true, query.getOrderNo() == OrderNo.ASC,
						(hasJoin ? "t." : "") + Constant.CLN_ORDER_NO);
			}
			if (!CollUtil.isEmpty(query.getOrderByFields())) {
				query.getOrderByFields().forEach(sValue -> wrapper.orderBy(true,
						sValue.getSType() == SType.ORDER_BY_ASC, (hasJoin ? "t." : "")
								+ sValue.getField().getColumnName()));
			}
			if (!CollUtil.isEmpty(query.getApplyList())) {
				for (int i = 0; i < query.getApplyList().size(); i++) {
					String alias = aliases[i];
					SApply<?, T> apply = query.getApplyList().get(i);
					if (!CollUtil.isEmpty(apply.getOrderByFields())) {
						apply.getOrderByFields().forEach(sValue -> wrapper.orderBy(true,
								sValue.getSType() == SType.ORDER_BY_ASC, alias + "." +
										sValue.getField().getColumnName()));
					}
				}
			}
		}
		return wrapper;
	}

	private static <T> void setQueryWrapper(Class<T> clazz, AbstractWrapper<?, String, ?> wrapper, SValue sValue) {
		setQueryWrapper(clazz, wrapper, sValue, null);
	}

	private static <T> void setQueryWrapper(Class<T> clazz, AbstractWrapper<?, String, ?> wrapper, SValue sValue, String alias) {
		if (wrapper == null || sValue == null || sValue.getSType() == null || sValue.getField() == null) {
			throw new RuntimeException(StrUtil.format("参数错误!wrapper={},value={}", wrapper, sValue));
		}
		SType sType = sValue.getSType();
		String fieldName = sValue.getField().getName();
		String tmpName = getColumnByBean(clazz, fieldName);
		String clnName = StrUtil.isNotEmpty(alias) ? alias + "." + tmpName : tmpName;

		// apply情况
		if (sValue.getValue().getClass() == SField.class) {
			@SuppressWarnings("unchecked")
			SField<T> value = (SField<T>) sValue.getValue();
			String clnValue = getColumnByBean(clazz, value.getName());

			// 允许的条件操作
			List<SType> canApplys = Arrays.asList(SType.EQ, SType.NE, SType.LT, SType.GT, SType.LE, SType.GE);
			if (!canApplys.contains(sType)) {
				throw new RuntimeException("apply暂不支持这个类型!QueryType=" + sType);
			}
			wrapper.apply(true, clnName + " " + sType.getKey() + " " + clnValue);
		} else {
			Object value = sValue.getValue();
			switch (sType) {
				case EQ:
					wrapper.eq(clnName, value);
					break;
				case NE:
					wrapper.ne(clnName, value);
					break;
				case LT:
					wrapper.lt(clnName, value);
					break;
				case GT:
					wrapper.gt(clnName, value);
					break;
				case LE:
					wrapper.le(clnName, value);
					break;
				case GE:
					wrapper.ge(clnName, value);
					break;
				case IN:
				case NOT_IN:
					Collection<?> cValue = getCValue(sValue);
					if (!(String.class == value.getClass() || Integer.class == value.getClass()
							|| Long.class == value.getClass() || value instanceof Collection)) {
						throw new RuntimeException("暂时不支持In类型!value=" + value);
					}
					if (CollUtil.isNotEmpty(cValue)) {
						if (sType == SType.IN) {
							wrapper.in(clnName, cValue);
						} else {
							wrapper.notIn(clnName, cValue);
						}
					} else if (sType == SType.IN && cValue != null) {
						// 如果为空集合，返回空，如果为null则不需要条件in
						wrapper.apply("1=2");
					}
					break;
				case LIKE:
					wrapper.like(clnName, value);
					break;
				case LIKE_RIGHT:
					wrapper.likeRight(clnName, value);
					break;
				case LIKE_LEFT:
					wrapper.likeLeft(clnName, value);
					break;
				case NOT_LIKE:
					wrapper.notLike(clnName, value);
					break;
				case IS_NULL:
					wrapper.isNull(clnName);
					break;
				case NOT_NULL:
					wrapper.isNotNull(clnName);
					break;
				case IS_EMPTY:
					if (wrapper instanceof UpdateWrapper) {
						@SuppressWarnings("unchecked")
						UpdateWrapper<T> updateWrapper = (UpdateWrapper<T>)wrapper;
						updateWrapper.and(child -> child.eq(clnName, "").or().isNull(clnName));
					} else if (wrapper instanceof QueryWrapper) {
						@SuppressWarnings("unchecked")
						QueryWrapper<T> newWrapper = (QueryWrapper<T>)wrapper;
						newWrapper.and(child -> child.eq(clnName, "").or().isNull(clnName));
					}
					break;
				case NOT_EMPTY:
					wrapper.ne(clnName, "");
					wrapper.isNotNull(clnName);
					break;
				case RANGE:
				case RANGE_NE:
				case NOT_RANGE:
					if (value instanceof Collection) {
						@SuppressWarnings("unchecked")
						Collection<Object> list = (Collection<Object>)value;
						Iterator<Object> temp = list.iterator();
						if (temp.hasNext()) {
							Object val = temp.next();
							if (val != null) {
								if (sType == SType.RANGE) {
									wrapper.ge(clnName, val);
								} else if (sType == SType.RANGE_NE) {
									wrapper.gt(clnName, val);
								} else {
									wrapper.lt(clnName, val);
								}
							}
							if (temp.hasNext()) {
								val = temp.next();
								if (val != null) {
									if (sType == SType.RANGE) {
										wrapper.le(clnName, val);
									} else if (sType == SType.RANGE_NE) {
										wrapper.lt(clnName, val);
									} else {
										wrapper.gt(clnName, val);
									}
								}
							}
						}
					} else {
						throw new RuntimeException("暂时不支持[Range,RangeNe,NotRange]类型!value=" + value);
					}
					break;
				case S_IN:
					// ids = 1,2,3 查找 4, 先查找 = 4, 再查 4,% 再查 %,4 接着查 %,4,%
					wrapper.and(child -> {
						child.eq(clnName, value);
						child.or().likeRight(clnName, value + StrUtil.COMMA);
						child.or().likeLeft(clnName, StrUtil.COMMA + value);
						child.or().like(clnName, StrUtil.COMMA + value + StrUtil.COMMA);
					});
					break;
				default:
					throw new RuntimeException("还没有实现没有这个类型!QueryType=" + sType);
			}
		}
	}

	public static <T> String toSql(QueryWrapper<T> wrapper) {
		String sql = wrapper.getCustomSqlSegment();
		for (Map.Entry<String, Object> entry : wrapper.getParamNameValuePairs().entrySet()) {
			sql = sql.replace("#{ew.paramNameValuePairs." + entry.getKey() + "}", formatValue(entry.getValue()));
		}
		return sql;
	}

	public static String formatValue(Object arg) {
		if (arg instanceof String) {
			return "'" + arg.toString().replace("'", "\\'") + "'";
		} else if (arg instanceof Date) {
			return "'" + DateUtil.format((Date) arg, DatePattern.NORM_DATETIME_PATTERN) + "'";
		} else if (arg != null) {
			return arg.toString();
		}
		return "''";
	}

	// 是否有join复杂SQL
	private <T extends SEntity<?>> boolean hasJoin(SQuery<T> query) {
		List<SApply<?, T>> applyList = query.getApplyList();
		return !CollUtil.isEmpty(applyList) && applyList.stream().anyMatch(s -> JoinType.isJoin(s.getJoinType()));
	}
	
	// 是否存在 order by id desc
	private static <T extends SEntity<?>> boolean isIdDesc(SQuery<T> query) {
		if (query.isNotOrderBy() || CollUtil.isEmpty(query.getOrderByFields())) {
			return false;
		}
		return query.getOrderByFields().stream().anyMatch(v -> v.getField().getName().equals(query.getPriKey())
				&& SType.ORDER_BY_DESC == v.getSType());
	}

	private static Collection<?> getCValue(SValue sValue) {
		Object value = sValue.getValue();
		if (String.class == value.getClass()) {
			return StrUtil.split(String.valueOf(value), StrUtil.COMMA);
		} else if (Integer.class == value.getClass() || Long.class == value.getClass()) {
			return Collections.singletonList(value);
		} else if (value instanceof Collection) {
			return (Collection<?>) value;
		}
		return null;
	}

	// 如果有不查询条件
	private <T extends SEntity<?>> boolean hasNoQuery(SQuery<T> query) {
		if (null == query || CollUtil.isEmpty(query.getQueryList())) {
			return false;
		}
		return query.getQueryList().stream().anyMatch(sValue -> {
			Collection<?> cValue = getCValue(sValue);
			// 如果为空集合，返回空，如果为null则不需要条件in
			return sValue.getSType() == SType.IN && cValue != null && CollUtil.isEmpty(cValue);
		});
	}
	
	// 默认不比较，不读取字段, Constant.CLN_STATUS
	public final static List<String> SPECIAL_COLUMNS = CollUtil.toList(
			Constant.CLN_CREATE_BY,
			Constant.CLN_MODIFY_BY,
			Constant.CLN_MODIFY_TIME,
			Constant.CLN_VERSION
	);

	private static String getTableName(Class<?> clazz) {
		TableName tableName = clazz.getAnnotation(TableName.class);
		if (tableName == null) {
			throw new RuntimeException("关联表不存在@TableName注解!clazz=" + clazz);
		}
		return tableName.value();
	}

	// 根据SField获得字段名字集合
	private List<String> getColumns(List<SField<?>> fieldList) {
		return CollUtil.isEmpty(fieldList) ? new ArrayList<>() : fieldList.stream().map(SField::getColumnName)
				.distinct().collect(Collectors.toList());
	}

	// 设置动态表名字
	private void setDynamicTableName(String tableName) {
		if (StrUtil.isNotEmpty(tableName)) {
			STable.setTableName(tableName);
		}
	}

	/**
	 * 更新类型
	 */
	enum Save {
		Insert, Update, Delete
	}

	/**
	 * 需要重新更新的字段和值
	 */
	@AllArgsConstructor
	static class ReUpdate {
		/** 字段名 */
		String fieldName;
		/** 字段值 */
		Object fieldValue;
	}
}
